//  
//  X11.cs
//  
//  Author:
//   Tsukanov Nikita <keks9n@gmail.com>
// 
//  Copyright (c) 2010-2011 Tsukanov Nikita
// 
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General public static License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
// 
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General public static License for more details.
// 
//  You should have received a copy of the GNU General public static License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.


using System;
using System.Runtime.InteropServices;
using System.Text;
namespace X11
{
	public static  partial class X
	{
		#region X11 functions
		[DllImport ("libX11")]
		public static extern  IntPtr XDefaultRootWindow(IntPtr display);
		[DllImport("libX11")]
		public static extern  void XMoveWindow(IntPtr Display, IntPtr Window, int x, int y);
		[DllImport("libX11")]
		public static extern  IntPtr XGetWMHints(IntPtr display, IntPtr w);
		[DllImport("libX11")]
		public static extern  void XSetWMHints(IntPtr display, IntPtr w, IntPtr hints);
		
		[DllImport ("libX11")]
		public static extern  IntPtr XOpenDisplay(IntPtr display);
		
		[DllImport ("libX11")]
		public static extern  int XFetchName(IntPtr display, IntPtr window, out IntPtr name);
		[DllImport ("libX11")]
		public static extern  int XRaiseWindow(IntPtr display, IntPtr window);
		
		[DllImport ("libX11")]
		public static extern  int XLowerWindow(IntPtr display, IntPtr window);
			
		[DllImport ("libX11")]
		public static extern  int XGetTransientForHint(IntPtr display, IntPtr window, out IntPtr ret);
		[DllImport ("libX11")]
		public static extern  int XDestroyWindow(IntPtr display, IntPtr window);	
		
		[DllImport ("libX11")]
		public static extern  int XDestroySubwindows(IntPtr display, IntPtr window);
		
		[DllImport ("libX11")]
		public static extern  void XSelectInput(IntPtr display, IntPtr window, long mask);
		
		[DllImport ("libX11")]
		public static extern  IntPtr XOpenDisplay(string display);	
		public delegate int XErrorHandler (IntPtr DisplayHandle, ref IntPtr error);
		[DllImport ("libX11")]
		public static extern  IntPtr XSetErrorHandler(XErrorHandler error_handler);
		[DllImport ("libX11")]
		public extern static int XFree(IntPtr data);
		[DllImport ("libX11")]
		public extern static int XPending(IntPtr display);
		[DllImport ("libX11")]
		public extern static void XInitThreads();
		[DllImport ("libX11")]
		public static extern  int XFlush(IntPtr display);
		[DllImport ("libX11")]
		public static extern  int XMoveResizeWindow(IntPtr display, IntPtr window, int x, int y, int width, int height);
		[DllImport ("libX11")]
		public static extern  uint XConfigureWindow(IntPtr display, IntPtr window, ChangeWindowFlags value_mask, ref XWindowChanges values);
		[DllImport ("libX11")]
		public static extern void XNextEvent(IntPtr display, byte[] ev);
		[DllImport ("libX11")]
		public static extern void XNextEvent(IntPtr display, IntPtr ev);
		[DllImport ("libX11")]
		public static extern void XMapWindow(IntPtr display, IntPtr win);
		[DllImport ("libX11")]
		public static extern void XUnmapWindow(IntPtr display, IntPtr win);
		                                      
		[DllImport ("libX11")]
		public static extern  IntPtr XInternAtom(IntPtr display, string atom_name, bool only_if_exists);
		[DllImport ("libX11")]
		public static extern  int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, IntPtr[] data, int nelements);
		[DllImport ("libX11")]
		public static extern  int XGetWindowProperty(IntPtr display, IntPtr window, IntPtr atom, IntPtr long_offset, IntPtr long_length,bool delete, IntPtr req_type, out IntPtr actual_type, out int actual_format, out IntPtr nitems, out IntPtr bytes_after, ref IntPtr prop);
		[DllImport ("libX11")]
		public static extern  void XGetWMNormalHints(IntPtr display, IntPtr window, ref XSizeHints hints, out IntPtr supplied_return);
		[DllImport ("libX11")]
		public static extern  void XSetWMNormalHints(IntPtr display, IntPtr window, ref XSizeHints hints);
		[DllImport("libX11")]
		public static extern  int XQueryTree (IntPtr display, IntPtr  w, out IntPtr root_return, out IntPtr  parent_return, out IntPtr children_return, out int nchildren_return);
		[DllImport("libX11")]
		public static extern  int XQueryTree (IntPtr display, IntPtr  w, out IntPtr root_return, out IntPtr  parent_return, IntPtr children_return, IntPtr nchildren_return);

		[DllImport ("libX11")]
		public static extern  int XGetWindowAttributes(IntPtr display, IntPtr window, ref XWindowAttributes attributes);
		[DllImport ("libX11")]
		public static extern  void XGetInputFocus(IntPtr display, out IntPtr focus_return,out int  revert_to_return);
		
		[DllImport ("libX11", EntryPoint="XSendEvent")]
		public static extern int XSendKeyEvent(IntPtr display, IntPtr window, bool propagate, IntPtr event_mask, ref XKeyEvent send_event);

				
		[DllImport ("libX11", EntryPoint="XSendEvent")]
		public static extern int XSendEvent(IntPtr display, IntPtr window, bool propagate, IntPtr event_mask, IntPtr send_event);

		[DllImport ("libX11")]
		public extern static void XReparentWindow (IntPtr display, IntPtr window, IntPtr new_parent,int x, int y);
		
		[DllImport ("libX11")]
		public static extern IntPtr XCreateSimpleWindow(IntPtr display, IntPtr parent, int x, int y,
		                                                 uint width, uint height, uint border_width,
		                                                 ulong border, ulong background);
		[DllImport ("libX11")]
		public static extern void XSynchronize(IntPtr display, bool enable);
		
		[DllImport ("libX11")]
		public static extern  int XSetInputFocus(IntPtr display, IntPtr window, RevertTo revert_to, ulong time);
		#endregion
		#region X11 structures
		public enum PropertyMode {
			Replace	= 0, Prepend, Append
		}	
		public enum RevertTo {	
			None= 0, PointerRoot, Parent
		}
		public enum StackMode {
			Above = 0,	Below,	TopIf,	BottomIf,	Opposite
		}
		public enum MapState {
			IsUnmapped	= 0, IsUnviewable,	IsViewable
		}
		[Flags]
		public enum ChangeWindowFlags {
			CWX				= 1<<0,
			CWY				= 1<<1,
			CWWidth			= 1<<2,
			CWHeight		= 1<<3,
			CWBorderWidth	= 1<<4,
			CWSibling		= 1<<5,
			CWStackMode		= 1<<6
		}
		
		[StructLayout(LayoutKind.Sequential)]
		public struct XWindowChanges {
			public int			x;
			public int			y;
			public int			width;
			public int			height;
			public int			border_width;
			public IntPtr		sibling;
			public StackMode	stack_mode;
		}	
		[Flags]
		public enum XSizeHintsFlags  {
			USPosition		= (1 << 0),
			USSize			= (1 << 1),
			PPosition		= (1 << 2),
			PSize			= (1 << 3),
			PMinSize		= (1 << 4),
			PMaxSize		= (1 << 5),
			PResizeInc		= (1 << 6),
			PAspect			= (1 << 7),
			PAllHints		= (PPosition | PSize | PMinSize | PMaxSize | PResizeInc | PAspect),
			PBaseSize		= (1 << 8),
			PWinGravity		= (1 << 9),
		}
	
		[StructLayout(LayoutKind.Sequential)]
		public struct XSizeHints {
			public IntPtr	flags;
			public int		x;
			public int		y;
			public int		width;
			public int		height;
			public int		min_width;
			public int		min_height;
			public int		max_width;
			public int		max_height;
			public int		width_inc;
			public int		height_inc;
			public int		min_aspect_x;
			public int		min_aspect_y;
			public int		max_aspect_x;
			public int		max_aspect_y;
			public int		base_width;
			public int		base_height;
			public int		win_gravity;
		}
	[StructLayout(LayoutKind.Sequential)]
	public struct XWindowAttributes {
		public int		x;
		public int		y;
		public int		width;
		public int		height;
		public int		border_width;
		public int		depth;
		public IntPtr		visual;
		public IntPtr		root;
		public int		c_class;
		public int	bit_gravity;
		public int	win_gravity;
		public int		backing_store;
		public IntPtr		backing_planes;
		public IntPtr		backing_pixel;
		public bool		save_under;
		public IntPtr		colormap;
		public bool		map_installed;
		public MapState	map_state;
		public IntPtr		all_event_masks;
		public IntPtr		your_event_mask;
		public IntPtr		do_not_propagate_mask;
		public bool		override_direct;
		public IntPtr		screen;


	}
	
		public enum XEventName 
		{
			KeyPress= 2,
			KeyRelease  = 3,
		}
		[StructLayout(LayoutKind.Sequential)]
		public struct XAnyEvent 
		{
			public XEventName  type;
			public IntPtr serial;
			public bool   send_event;
			public IntPtr display;
			public IntPtr window;
		}
		[StructLayout(LayoutKind.Sequential)]
		public struct XKeyEvent 
		{
			public XEventName type;
			public IntPtr serial;
			public bool send_event;
			public IntPtr display;
			public IntPtr window;
			public IntPtr root;
			public IntPtr subwindow;
			public IntPtr time;
			public int x;
			public int y;
			public int x_root;
			public int y_root;
			public int state;
			public int keycode;
			public bool same_screen;
		}

		#endregion
		
		public static  IntPtr XA_STRING=new IntPtr(31);
		public static IntPtr UTF8_STRING, WM_NAME, _NET_WM_NAME, _NET_CLIENT_LIST_STACKING, WM_CLASS, WM_WINDOW_ROLE;
		
		public static  IntPtr Display;
		public static bool OpenDisplay(string Name)
		{
			Init();
			if (Name==null)
				Display=X11.X.XOpenDisplay (IntPtr.Zero);
			else
				Display=X11.X.XOpenDisplay(Name);
			if(Display.ToInt64()!=0)
			{
				ConnectToDisplay(Display);
			}
			
			
			return (Display.ToInt64()!=0);
			
		}
		
		public static void ConnectToDisplay(IntPtr display)
		{
			Display=display;
			UTF8_STRING=XInternAtom(Display,"UTF8_STRING", false);
			WM_NAME=XInternAtom(Display,"WM_NAME", false);
			_NET_WM_NAME=XInternAtom(Display,"_NET_WM_NAME", false);
			_NET_CLIENT_LIST_STACKING=XInternAtom(Display, "_NET_CLIENT_LIST_STACKING", false);
			WM_CLASS=XInternAtom(Display, "WM_CLASS", false);
			WM_WINDOW_ROLE=XInternAtom(Display, "WM_WINDOW_ROLE", false);
		}
		
		public static  void Init ()
		{
			
			XSetErrorHandler (ErrorHandler);
			
			
		}
		public static  int ErrorHandler (IntPtr DisplayHandle, ref IntPtr error)
		{
			Console.WriteLine("XERROR");
			return 0;
		}
				
		
		public static  bool GetUrgencyHint (IntPtr w)
		{
			var Hints = XGetWMHints (Display, w);
			long Val = Marshal.ReadIntPtr (Hints).ToInt64 ();
			XFree (Hints);
			long Mask = 1 << 8;
			return ((Val & Mask) != 0);
			
		}
		

		public static  void SetUrgencyHint (IntPtr w, bool Value)
		{
			var Hints = XGetWMHints (Display, w);
			long Val = Marshal.ReadIntPtr (Hints).ToInt64 ();
			long Mask = 1 << 8;
			Val |= Mask;
			if (!Value)
				Val ^= Mask;
			Marshal.WriteIntPtr (Hints, new IntPtr (Val));
			XSetWMHints (Display, w, Hints);
			
			XFlush (Display);
			XFree (Hints);
		}

		
		
		public static  void MoveAndResize (IntPtr w, int x, int y, int width, int height)
		{
			var chg = new XWindowChanges ();
			
			chg.x = x;
			chg.y = y;
			chg.width = width;
			chg.height = height;
			
			
			XMoveResizeWindow (Display, w, x, y, width, height);
			XConfigureWindow (Display, w, (ChangeWindowFlags)(3 | (int)ChangeWindowFlags.CWHeight | (int)ChangeWindowFlags.CWWidth),
				ref chg);
			
			XFlush(Display);
		}

		
		public static  void HideWindow (IntPtr Window)
		{
			
			var data = new IntPtr[] { XInternAtom (Display, "_NET_WM_STATE_SKIP_TASKBAR", true),
										XInternAtom (Display, "_NET_WM_STATE_SKIP_PAGER", true) };
			
			XChangeProperty (Display, Window, XInternAtom (Display, "_NET_WM_STATE", true)
				, new IntPtr (4), 32, PropertyMode.Replace, data, 2);
			XFlush (Display);
		}

		
		
	
		public static  void SetMinSize (IntPtr Window, int W, int H)
		{
			var hints = new XSizeHints ();
			IntPtr Sup;
			
			hints.flags = new IntPtr ((long)XSizeHintsFlags.PAllHints);
			XGetWMNormalHints (Display, Window, ref hints, out Sup);
			
			var flags = hints.flags.ToInt64 ();
			flags = flags |(long) XSizeHintsFlags.PMinSize;
			hints.flags = new IntPtr (flags);
			hints.min_width = W;
			hints.min_height = H;
			
			
			
			XSetWMNormalHints (Display, Window, ref hints);
		}

		public static  IntPtr GetTopWindow ()
		{
			IntPtr Window;
			int wtf;
			XGetInputFocus (Display, out Window, out wtf);
		
			return Window;
		}
		
		public static  IntPtr GetRootWindow ()
		{
			return XDefaultRootWindow (Display);
		}
		
		public static  void Unfocus ()
		{
			var TS = (int)(DateTime.UtcNow - new DateTime (1970, 1, 1, 0, 0, 0)).TotalSeconds;
			
			XSetInputFocus (Display, IntPtr.Zero, RevertTo.None, (ulong)TS);
		}
		
		public static  IntPtr GetParent (IntPtr Window)
		{
			IntPtr root, parent, children;
			int nchildren;
			XQueryTree (Display, Window, out root, out parent, out children, out nchildren);
			XFree (children);
			return parent;
		}
		public static System.Collections.Generic.List<IntPtr> GetChildren (IntPtr Window)
		{
			IntPtr root, parent, children;
			int nchildren;
			var rv=new System.Collections.Generic.List<IntPtr>();
			XQueryTree (Display, Window, out root, out parent, out children, out nchildren);
			for(int i=0; i<nchildren; i++)
			{
				rv.Add(Marshal.ReadIntPtr(children, IntPtr.Size*i));
			}
			
			XFree (children);
			return rv;
			
		}
		
		public static System.Collections.Generic.List<IntPtr> GetTopLevels()
		{
			IntPtr actual_type, nitems, bytes_after, propval=IntPtr.Zero;
			int ret_format;
			XGetWindowProperty (Display,GetRootWindow(), _NET_CLIENT_LIST_STACKING, IntPtr.Zero,
					new IntPtr (1024), false,IntPtr.Zero , out actual_type, out ret_format, out nitems,
			out bytes_after, ref propval);
			var rv=new System.Collections.Generic.List<IntPtr>();
			for(int c=0; c<nitems.ToInt64(); c++)
			{
				rv.Add(Marshal.ReadIntPtr(propval,IntPtr.Size*c));
			}
			XFree(propval);
			return rv;
			
		}
		
		public static string GetWindowClass(IntPtr Window)
		{
			var res=GetStringAtom(Window,WM_CLASS, false);
			if(res==null)
				res="";
			return res.Split((char)0)[0];
		}
		public static string GetWindowRole(IntPtr Window)
		{
			return GetStringAtom(Window,WM_WINDOW_ROLE, false);

		}
		
		public static System.Collections.Generic.List<IntPtr> GetUnmanagedTopLevels()
		{
			var rv=new System.Collections.Generic.List<IntPtr>();
			foreach (IntPtr w in GetChildren(GetRootWindow()))
			{
				XWindowAttributes attrs=new XWindowAttributes();
				XGetWindowAttributes(Display,w,ref attrs);
				if ((attrs.map_state==MapState.IsViewable))
					if(!attrs.override_direct)
						rv.Add(w);
			}
			return rv;
		}
		

		
		private static void WalkWinTree(IntPtr Window, System.Collections.Generic.List<IntPtr> list)
		{
			foreach(var ch in GetChildren(Window))
			{
				list.Add(ch);
				WalkWinTree(ch, list);
			}
		}
		public static System.Collections.Generic.List<IntPtr> GetAllChildren(IntPtr window)
		{
			var rv=new System.Collections.Generic.List<IntPtr>();
			WalkWinTree(window, rv);
			return rv;
			
		}
		public static System.Collections.Generic.List<IntPtr> GetAllWindows()
		{
			return GetAllChildren(GetRootWindow());
			
		}
		
		
		
		public static string GetStringAtom (IntPtr Window, IntPtr Atom, bool Utf8)
		{
			IntPtr actual_type, nitems, bytes_after, propval=IntPtr.Zero;
			int ret_format;
			XGetWindowProperty (Display, Window, Atom, IntPtr.Zero,
					new IntPtr (1024), false, Utf8?UTF8_STRING:XA_STRING, out actual_type, out ret_format, out nitems,
			out bytes_after, ref propval);
			
			if(propval.ToInt64()==0) return null;
			
			var ret_nitems=nitems.ToInt64();
			
		var tmp_size = (ret_format / 8) * ret_nitems;
		//if(ret_format==32) tmp_size *= sizeof(long)/4;
			
		var Role=Marshal.PtrToStringAnsi(propval, (int)tmp_size);
			XFree(propval);
			return Role;
		}
		
		public static string GetStringAtom (IntPtr Window, string Atom, bool Utf8)
		{
			return GetStringAtom(Window, XInternAtom(Display,Atom,false), Utf8);
		}
		
		public static void SetInputFocus(IntPtr Window)
		{
			XSetInputFocus(Display, Window, RevertTo.None,0);
			XFlush(Display);
		}
		
		public static string GetWindowName(IntPtr window)
		{
			string rv=GetStringAtom(window, _NET_WM_NAME, true);
			if(rv==null)
				rv=GetStringAtom(window, "WM_NAME", false);
			return rv;
		}
		
		public static void RaiseWindow(IntPtr window)
		{
			XRaiseWindow(Display, window);
			XFlush(Display);
		}
		
		public static void LowerWindow(IntPtr window)
		{
			XLowerWindow(Display, window);
			XFlush(Display);
		}
		
		public static void DestroyWindow(IntPtr window)
		{
			XDestroyWindow(Display, window);
			XFlush(Display);
		}
				
		public static void DestroySubwindows(IntPtr window)
		{
			XDestroySubwindows (Display, window);
			XFlush(Display);
		}
	
		public static void Flush()
		{
			XFlush(Display);
		}
		public static int PendingEvents
		{
			get
			{
				return XPending(Display);
			}
		}
	
	}
	
}


